package org.mcjug.locationmailer.service;

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

import org.mcjug.locationmailer.R;
import org.mcjug.locationmailer.db.StatusMessageDatabaseHelper;
import org.mcjug.locationmailer.model.StatusMessage;
import org.mcjug.locationmailer.util.EmailUtil;
import org.mcjug.locationmailer.util.LocationUtil;

import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Bundle;
import android.os.IBinder;

public class LocationService extends Service {
	private static final String TAG = LocationService.class.getSimpleName();
	private static final int LOCATION_TIMEOUT = 1000 * 60 * 2;
	
	public static final String NEW_LOCATION_INTENT_ACTION = "org.mcjug.locationmailer.NEW_LOCATION";
	public static final String RECEIVE_LOCATION_NOTIFICATIONS = "org.mcjug.locationmailer.RECEIVE_LOCATION_NOTIFICATIONS";
	public static final String SEND_LOCATION_NOTIFICATIONS = "org.mcjug.locationmailer.SEND_LOCATION_NOTIFICATIONS";

	private LocationManager locationManager;
	private ConnectivityManager connectivityManager;
	private ConnectivityReceiver connectivityReceiver;
	
	private Location currentLocation;
	private StatusMessageDatabaseHelper statusMessageHelper;
	private StatusMessage statusMessage;
	
	private Timer timer;
	
	public int onStartCommand(Intent intent, int flags, int startId) {		statusMessageHelper = new StatusMessageDatabaseHelper(getApplicationContext());
		
		// Timer for acquiring a location
	    timer = new Timer();
        timer.schedule(new LocationTimeoutTask(), LOCATION_TIMEOUT);
 
		setupLocationUpdates();
		
		return START_NOT_STICKY;
	}

	private void setupLocationUpdates() {
		// Clear out current location
		currentLocation = null;
		
		// Acquire a reference to the system Location Manager
		locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
		
		// Register location listener with the Location Manager to receive location updates
		locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 10000, 1000, locationListener);
		locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 10000, 1000, locationListener);
	}	
	
	private void registerForConnectivityUpdates() {
		// Register to receive connectivity broadcasts
		connectivityReceiver = new ConnectivityReceiver();	
		registerReceiver(connectivityReceiver, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
	}
	
	// Check connectivity status
	private boolean checkConnectivity() {
		// Acquire a reference to the system Connectivity Manager
		connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
		
		boolean isAvailable = connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE).isAvailable();
		if (!isAvailable) {
			isAvailable = connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI).isAvailable();
		}
		return isAvailable;
	}	

	// Define a listener that responds to location updates
	private LocationListener locationListener = new LocationListener() {

		// Called when a new location is found by the location provider.
		public void onLocationChanged(Location newLocation) {
			currentLocation = newLocation;
			if (newLocation.getProvider().equals(LocationManager.GPS_PROVIDER)) {
				locationManager.removeUpdates(this);
				if (checkConnectivity()) {
					timer.cancel();
					if (!locationMailerThread.isAlive()) {
						locationMailerThread.start();
					}
				} else {
					registerForConnectivityUpdates();
				}
			}
		}

		public void onStatusChanged(String provider, int status, Bundle extras) {
		}

		public void onProviderEnabled(String provider) {
		}

		public void onProviderDisabled(String provider) {
		}
	};
	
	// Location info to store in database
	private StatusMessage getStatusMessage(Location location) {
		statusMessage = new StatusMessage();
		statusMessage.setCreatedDate(new Date());
		statusMessage.setAddress(LocationUtil.getAddress(location, getApplicationContext()));
		statusMessage.setLocation(location);
		statusMessage.setMessage("");
		return statusMessage;		
	}
	
	// Listener for Connectivity state
	private class ConnectivityReceiver extends BroadcastReceiver {
		public void onReceive(Context context, Intent intent) {
			NetworkInfo info = intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
			if (info != null) {
				NetworkInfo.State state = info.getState();
				if (state == NetworkInfo.State.CONNECTED || state == NetworkInfo.State.CONNECTING) {
					unregisterReceiver(connectivityReceiver);
					connectivityReceiver = null;
					if (!locationMailerThread.isAlive()) {
						locationMailerThread.start();
					}
				}
			}
		}
	}

	private class LocationTimeoutTask extends TimerTask {
		public void run() {
			updateLocation();
		}
	}

	private Thread locationMailerThread = new Thread(new Runnable() {
		public void run() {
			updateLocation();
		}
	});
	
	public void updateLocation() {
		cleanup();
		
		statusMessage = getStatusMessage(currentLocation);
		if (currentLocation == null) {
	        statusMessage.setMessage(getString(R.string.msgNoLocation));
		} else if (!checkConnectivity()){
		    statusMessage.setMessage(getString(R.string.msgNoConnectivity));
		} else {
		    EmailUtil.sendEmail(statusMessage, getApplicationContext());
		}
		
	    // Store status message in database
	    statusMessageHelper.insertOrIgnore(statusMessage);
	    
	    // Broadcast location to activity to show status message in location list
		sendBroadcast(new Intent(NEW_LOCATION_INTENT_ACTION), RECEIVE_LOCATION_NOTIFICATIONS);
		LocationService.this.stopSelf();
	}
	
	public IBinder onBind(Intent arg0) {
		return null;
	}
	
	private void cleanup() {
		locationManager.removeUpdates(locationListener);
		if (connectivityReceiver != null) {
		    unregisterReceiver(connectivityReceiver);
		}
	}
	 
	public void onDestroy() {
		super.onDestroy();
	}
}
